#ifndef VEC3_HPP
#define VEC3_HPP

#include "math.h"
//#include "aiVector3D.h"
#include "Vec2.hpp"

template <typename T>
class VEC3
{
public:
    // direct variable access
    T    x, y, z;
    
    // initialization
    VEC3 ( );
    VEC3 (T a);
    VEC3 (T a, T b, T c);
    template <typename U>
    VEC3 (const VEC3<U> &v);
//	VEC3 (const aiVector3D &v);   // conversion from Assimp 3D vector
    
    // operators
    VEC3 operator = (const VEC3 &v);            // assignment
    VEC3 operator + (const VEC3 &v) const;        // addition, by element
    VEC3 operator - (const VEC3 &v) const;        // subtraction, by element
    T    operator * (const VEC3 &v) const;        // dot product
    VEC3 operator * (const T s) const;            // multiplication by a scalar
    VEC3 operator / (const T s) const;            // division by a scalar
    VEC3 operator - () const;                    // negation
    T&   operator[] (const int i);                // access of members
    bool operator ==(const VEC3<T> &v) const;         // equality test
    
    // named operation functions
    T dot(const VEC3 &v) const;
    VEC3 cross(const VEC3 &v) const;
    T length() const;
    T squareLength() const;
    VEC3 normalized () const;
    void normalize();
    
};

// I REALLY DO HATE this... sigh

// initialization
template  <typename T>
VEC3<T>::VEC3 ( )
{
    x = (T)0.0;
    y = (T)0.0;
    z = (T)0.0;
}

template  <typename T>
VEC3<T>::VEC3 (T a)
{
    x = a;
    y = a;
    z = a;
}

template  <typename T>
VEC3<T>::VEC3 (T a, T b, T c)
{
    x = a;
    y = b;
    z = c;
}

template  <typename T>
template  <typename U>
VEC3<T>::VEC3 (const VEC3<U> &v)
{
    x = (T)v.x;
    y = (T)v.y;
    z = (T)v.z;
}

/*
template <typename T>
VEC3<T>::VEC3 (const aiVector3D &v)
{
	x = v.x;
	y = v.y;
	z = v.z;
}
*/

// operators
template  <typename T>
VEC3<T> VEC3<T>::operator = (const VEC3<T> &v)            // assignment
{
    x = v.x;
    y = v.y;
    z = v.z;
    return (*this);
}

template  <typename T>
VEC3<T> VEC3<T>::operator + (const VEC3<T> &v) const        // addition, by element
{
    return VEC3(x + v.x, y + v.y, z + v.z);
}

template  <typename T>
VEC3<T> VEC3<T>::operator - (const VEC3<T> &v) const        // subtraction, by element
{
    return VEC3(x - v.x, y - v.y, z - v.z);
}

template  <typename T>
T    VEC3<T>::operator * (const VEC3<T> &v) const        // dot product
{
    return x * v.x + y * v.y + z * v.z;
}

template  <typename T>
VEC3<T> VEC3<T>::operator * (T s) const    // multiplication by a scalar
{
    return VEC3(x * s, y * s, z * s);
}

template  <typename T>
VEC3<T> VEC3<T>::operator / (T s) const    // division by a scalar
{
    return VEC3(x / s, y / s, z / s);
}

template  <typename T>
VEC3<T> VEC3<T>::operator - () const            // negation
{
    return VEC3(-x, -y, -z);
}

template  <typename T>
T& VEC3<T>::operator[] (const int i)        // access if of members
{
    if (i <= 0)
        return &x;
    if (i == 1)
        return &y;
    if (i >= 2)
        return &z;
}

template  <typename T>
bool VEC3<T>::operator ==(const VEC3<T> &v) const         // equality test
{
    return (x == v.x && y == v.y && z == v.z);
}


// named operation functions

template  <typename T>
T VEC3<T>::dot(const VEC3<T> &v) const
{
    return x * v.x + y * v.y + z * v.z;
}

template  <typename T>
VEC3<T> VEC3<T>::cross(const VEC3<T> &v) const
{
    VEC3<T> result;
    result.x = (y * v.z - z * v.y);
    result.y = (z * v.x - x * v.z);
    result.z = (x * v.y - y * v.x);
    return result;
}

template <typename T>
T VEC3<T>::length() const
{
    return (T)sqrt((double)x*x + (double)y*y + (double)z*z);
}

template <typename T>
T VEC3<T>::squareLength() const
{
    return (T)((double)x*x + (double)y*y + (double)z*z);
}

template  <typename T>
VEC3<T> VEC3<T>::normalized () const
{
    T len = this->length();
    if (len < 0.00001)
        return VEC3(0, 0, 0);
    return VEC3(x / len, y / len, z / len);

}

template  <typename T>
void VEC3<T>::normalize()
{
    T len = this->length();
    if (len < 0.00001)
        return;
    x = x / len;
    y = y / len;
    z = z / len;
}

#endif
